package org.deri.socializer.com.xmpp;

import java.util.HashMap;
import java.util.Iterator;

import org.deri.socializer.SocializerApplication;
import org.deri.socializer.activities.chat.ChatActivity;
import org.deri.socializer.activities.chat.ParticipantChat;
import org.deri.socializer.util.XMPPUtilities;
import org.jivesoftware.smack.AccountManager;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;

import android.util.Log;



/**
 * @author christian
 *
 */
public class XmppClient {

	private SocializerApplication app					= null;
	private ChatActivity chatActivity					= null;
	private XmppUser xmppUser							= null;
	private XmppMessageHandler xmppMessageHandler 		= null;
	private XMPPConnection xmppConnection 				= null;
	private HashMap<String, XmppGroupChat> groupChats	= null;
	private HashMap<String, XmppPrivateChat> chats		= null;
	private PacketListener presenceListener				= null;
	private PacketListener messageListener				= null;
	private int port									= 5222;
	private XmppChat lastXmppWebChat					= null;
	private XmppChat lastXmppGeoChat					= null;






	public XmppClient(SocializerApplication app){
		this.app = app;
		this.groupChats = new HashMap<String, XmppGroupChat>();
		this.chats = new HashMap<String, XmppPrivateChat>();
	}
	
	
	public void setXMPPMessageHandler(XmppMessageHandler xmppMessageHandler) {
		this.xmppMessageHandler = xmppMessageHandler;
	}
	
	public boolean login(XmppUser xmppUser, boolean doRegister) {
		this.xmppUser = xmppUser;
		try {
			ConnectionConfiguration config = new ConnectionConfiguration(xmppUser.getHost(), this.port, xmppUser.getResource());
			this.xmppConnection = new XMPPConnection(config);
			this.xmppConnection.connect();			
		}	catch (XMPPException e) {
			System.err.println("Error: unable to establish xmpp connection");
			e.printStackTrace();
			Log.e("Socializer", e.toString());
			return false;
		}	
			
		boolean success = false;
		try {	
			this.xmppConnection.login(xmppUser.getUser(), xmppUser.getPassword());
			success = true;
		} catch (XMPPException e) {
			if (doRegister)
				success = this.checkAndRegister(this.xmppConnection, this.xmppUser);
		}	
			
		if (success) {			
			PacketFilter presenceFilter = new PacketTypeFilter(Presence.class);
			this.presenceListener = new PacketListener(){
				@Override
				public void processPacket(Packet packet) {
					if (packet instanceof Presence)
						handlePresence((Presence)packet);
				}
			};
			this.xmppConnection.addPacketListener(this.presenceListener, presenceFilter);
			
			PacketFilter messageFilter = new PacketTypeFilter(Message.class);
			this.messageListener = new PacketListener(){
				@Override
				public void processPacket(Packet packet) {
					if (packet instanceof Message)
						handleMessage((Message)packet);
				}
			};
			this.xmppConnection.addPacketListener(this.messageListener, messageFilter);
			
			return true;
		} else {
			return false;
		}
		
	}
	
	
	public boolean checkAndRegister(XMPPConnection currentConn, XmppUser user){
	    if (!currentConn.isAuthenticated()){
	        AccountManager acManager = currentConn.getAccountManager();
	        try {
	            acManager.createAccount(user.getUser(), user.getPassword());
	        } catch (XMPPException e) {
	            return false;
	        }
	        return true;
	    }else{
	        return true;
	    }
	}

	public void leaveAllGroupChats() {
		Iterator<XmppGroupChat> iter = this.groupChats.values().iterator();
		while (iter.hasNext()) {
			iter.next().leave();
		}
		this.groupChats.clear();
	}
	
	public void closeAllPrivateChats() {
		this.groupChats.clear();
	}
	
	public HashMap<String, XmppGroupChat> getGroupChats() {
		return groupChats;
	}


	public HashMap<String, XmppPrivateChat> getPrivateChats() {
		return chats;
	}



	public boolean isConnected() {
		if (this.xmppConnection == null)
			return false;
		return this.xmppConnection.isConnected();
	}
	
	public void disconnect() {
		this.leaveAllGroupChats();
		this.xmppConnection.disconnect();
    }

	
	public XmppUser getXMPPUser() {
		return this.xmppUser;
	}
	
	public XMPPConnection getXMPPConnection() {
		return this.xmppConnection;
	}
	
	public XmppGroupChat getXmppWebGroupChat(String room) {
		return this.getXmppGroupChat(room + SocializerApplication.XMPP_GROUP_CHAT_SUFFIX_WEB);
	}

	public XmppGroupChat getXmppGebGroupChat(String room) {
		return this.getXmppGroupChat(room + SocializerApplication.XMPP_GROUP_CHAT_SUFFIX_GEO);
	}

	
	public XmppGroupChat getXmppGroupChat(String room) {
		System.out.println("[XmppClient.getGetXmppGroupChat] room = " + room);
		if (this.groupChats.containsKey(room))
			return this.groupChats.get(room);
		else 
			return null;
	}
	
	public XmppPrivateChat getXmppPrivateChat(String userJid, boolean createIfNotFound) {
		String userAddress = XMPPUtilities.getSenderAddress(userJid);
		if (this.chats.containsKey(userAddress)) {
			return this.chats.get(userAddress);
		} else {
			if (createIfNotFound) {
				ParticipantChat participant = new ParticipantChat(userAddress);
				XmppPrivateChat newPrivateChat = new XmppPrivateChat(this, participant);
				this.chats.put(userAddress, newPrivateChat);
				return newPrivateChat;
			} else {
				return null;
			}
		}
	}
	
	public void closePrivateChat(String userJid) {
		String userAddress = XMPPUtilities.getSenderAddress(userJid);
		if (this.chats.containsKey(userAddress)) {
			this.chats.remove(userAddress);
		}
	}
	
	
	public XmppGroupChat createGroupChat(String room) {
		if (!this.groupChats.containsKey(room)) {
			XmppGroupChat groupChat = new XmppGroupChat(this, room);
			this.groupChats.put(room, groupChat);
			return groupChat;
		} else {
			return this.groupChats.get(room);
		}
	}
	
	public XmppGroupChat createWebGroupChat(String room) {
		return this.createGroupChat(room + SocializerApplication.XMPP_GROUP_CHAT_SUFFIX_WEB);
	}
	
	public XmppGroupChat createGeoGroupChat(String room) {
		return this.createGroupChat(room + SocializerApplication.XMPP_GROUP_CHAT_SUFFIX_GEO);
	}
	
	
	public void joinGroupChat(String room) {
		if (this.groupChats.containsKey(room)) {
			this.groupChats.get(room).join();
		}
	}
	
	public void joinWebGroupChat(String room) {
		this.joinGroupChat(room + SocializerApplication.XMPP_GROUP_CHAT_SUFFIX_WEB);
	}

	public void joinGeoGroupChat(String room) {
		System.out.println("[XmppClient.joinGeoGroupChat] " + room + SocializerApplication.XMPP_GROUP_CHAT_SUFFIX_GEO);
		this.joinGroupChat(room + SocializerApplication.XMPP_GROUP_CHAT_SUFFIX_GEO);
	}

	
	public void leaveGroupChat(String room) {
		if (this.groupChats.containsKey(room)) {
			this.groupChats.get(room).leave();
			this.groupChats.remove(room);
		}
	}


	public void leaveWebGroupChat(String room) {
		this.leaveGroupChat(room + SocializerApplication.XMPP_GROUP_CHAT_SUFFIX_WEB);
	}
	
	public void leaveGeoGroupChat(String room) {
		this.leaveGroupChat(room + SocializerApplication.XMPP_GROUP_CHAT_SUFFIX_GEO);
	}
	
	
	public XmppGroupChat getCurrentWebGroupChat() {
		if (this.lastXmppWebChat instanceof XmppGroupChat)
			return (XmppGroupChat)this.lastXmppWebChat;
		else
			return null;
	}
	
	public XmppGroupChat getCurrentGeoGroupChat() {
		if (this.lastXmppGeoChat instanceof XmppGroupChat)
			return (XmppGroupChat)this.lastXmppGeoChat;
		else
			return null;
	}
	
	public void handleChatMessage(Message message) {
		this.xmppMessageHandler.handleChatMessage(message);
	}
	
	public void handleWebGroupChatMessage(Message message) {
		this.xmppMessageHandler.handleGroupChatMessage(message);
	}
	
	public void handleWebGroupChatPresence(Presence presence) {
		this.xmppMessageHandler.handleWebGroupChatPresence(presence);
	}


	
	public void handleMessage(Message message) {
		if (message.getType() == Message.Type.chat) {
			this.handleWebMessage(message);
		} else if (message.getType() == Message.Type.groupchat) {
			String room = XMPPUtilities.getRoomName(message.getFrom());
			System.out.println(room);
			if (room.endsWith(SocializerApplication.XMPP_GROUP_CHAT_SUFFIX_WEB)) {
				this.handleWebMessage(message);
			}
		}
	}

	
	public void handleWebMessage(Message message) {
		if (message.getType() == Message.Type.chat) {
			String sender = XMPPUtilities.getSenderAddress(message.getFrom());
			System.out.println("[XmppClient.handleWebMessage] chat sender = " + sender);
			if (!this.chats.containsKey(sender))  
				this.chats.put(sender, new XmppPrivateChat(this, new ParticipantChat(sender)));
			this.chats.get(sender).handleMessage(message);
			this.xmppMessageHandler.handleChatMessage(message);
		} else if (message.getType() == Message.Type.groupchat) {
			String room = XMPPUtilities.getRoomName(message.getFrom());
			if (this.groupChats.containsKey(room)) {
				this.groupChats.get(room).handleMessage(message);
			}
			this.xmppMessageHandler.handleGroupChatMessage(message);
		}
		
		
	}
	
	
	private void handlePresence(Presence presence) {
		String sender = XMPPUtilities.getRoomName(presence.getFrom());
		if (this.groupChats.containsKey(sender)) {
			XmppGroupChat groupChat = this.groupChats.get(sender); 
			groupChat.handlePresence(presence);
			if (groupChat.getCurrentRoom().equalsIgnoreCase(this.getCurrentWebGroupChat().getRoomName())) 
				this.xmppMessageHandler.handleWebGroupChatPresence(presence);
		}
	}


	public XmppChat getLastXmppWebChat() {
		return lastXmppWebChat;
	}


	public void setLastXmppWebChat(XmppChat lastXmppChat) {
		this.lastXmppWebChat = lastXmppChat;
	}
	
	public XmppChat getLastXmppGeoChat() {
		return lastXmppGeoChat;
	}


	public void setLastXmppGeoChat(XmppChat lastXmppChat) {
		this.lastXmppGeoChat = lastXmppChat;
	}
	

	public ChatActivity getChatActivity() {
		return chatActivity;
	}


	public void setChatActivity(ChatActivity chatActivity) {
		this.chatActivity = chatActivity;
	}

	public SocializerApplication getApp() {
		return app;
	}


}
